home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkPlace.c < prev    next >
C/C++ Source or Header  |  1995-06-04  |  30KB  |  1,024 lines

  1. /* 
  2.  * tkPlace.c --
  3.  *
  4.  *    This file contains code to implement a simple geometry manager
  5.  *    for Tk based on absolute placement or "rubber-sheet" placement.
  6.  *
  7.  * Copyright (c) 1992-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  */
  13.  
  14. static char sccsid[] = "@(#) tkPlace.c 1.21 95/06/04 14:23:50";
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18.  
  19. /*
  20.  * Border modes for relative placement:
  21.  *
  22.  * BM_INSIDE:        relative distances computed using area inside
  23.  *            all borders of master window.
  24.  * BM_OUTSIDE:        relative distances computed using outside area
  25.  *            that includes all borders of master.
  26.  * BM_IGNORE:        border issues are ignored:  place relative to
  27.  *            master's actual window size.
  28.  */
  29.  
  30. typedef enum {BM_INSIDE, BM_OUTSIDE, BM_IGNORE} BorderMode;
  31.  
  32. /*
  33.  * For each window whose geometry is managed by the placer there is
  34.  * a structure of the following type:
  35.  */
  36.  
  37. typedef struct Slave {
  38.     Tk_Window tkwin;        /* Tk's token for window. */
  39.     struct Master *masterPtr;    /* Pointer to information for window
  40.                  * relative to which tkwin is placed.
  41.                  * This isn't necessarily the logical
  42.                  * parent of tkwin.  NULL means the
  43.                  * master was deleted or never assigned. */
  44.     struct Slave *nextPtr;    /* Next in list of windows placed relative
  45.                  * to same master (NULL for end of list). */
  46.  
  47.     /*
  48.      * Geometry information for window;  where there are both relative
  49.      * and absolute values for the same attribute (e.g. x and relX) only
  50.      * one of them is actually used, depending on flags.
  51.      */
  52.  
  53.     int x, y;            /* X and Y pixel coordinates for tkwin. */
  54.     float relX, relY;        /* X and Y coordinates relative to size of
  55.                  * master. */
  56.     int width, height;        /* Absolute dimensions for tkwin. */
  57.     float relWidth, relHeight;    /* Dimensions for tkwin relative to size of
  58.                  * master. */
  59.     Tk_Anchor anchor;        /* Which point on tkwin is placed at the
  60.                  * given position. */
  61.     BorderMode borderMode;    /* How to treat borders of master window. */
  62.     int flags;            /* Various flags;  see below for bit
  63.                  * definitions. */
  64. } Slave;
  65.  
  66. /*
  67.  * Flag definitions for Slave structures:
  68.  *
  69.  * CHILD_WIDTH -        1 means -width was specified;
  70.  * CHILD_REL_WIDTH -        1 means -relwidth was specified.
  71.  * CHILD_HEIGHT -        1 means -height was specified;
  72.  * CHILD_REL_HEIGHT -        1 means -relheight was specified.
  73.  */
  74.  
  75. #define CHILD_WIDTH        1
  76. #define CHILD_REL_WIDTH        2
  77. #define CHILD_HEIGHT        4
  78. #define CHILD_REL_HEIGHT    8
  79.  
  80. /*
  81.  * For each master window that has a slave managed by the placer there
  82.  * is a structure of the following form:
  83.  */
  84.  
  85. typedef struct Master {
  86.     Tk_Window tkwin;        /* Tk's token for master window. */
  87.     struct Slave *slavePtr;    /* First in linked list of slaves
  88.                  * placed relative to this master. */
  89.     int flags;            /* See below for bit definitions. */
  90. } Master;
  91.  
  92. /*
  93.  * Flag definitions for masters:
  94.  *
  95.  * PARENT_RECONFIG_PENDING -    1 means that a call to RecomputePlacement
  96.  *                is already pending via a Do_When_Idle handler.
  97.  */
  98.  
  99. #define PARENT_RECONFIG_PENDING    1
  100.  
  101. /*
  102.  * The hash tables below both use Tk_Window tokens as keys.  They map
  103.  * from Tk_Windows to Slave and Master structures for windows, if they
  104.  * exist.
  105.  */
  106.  
  107. static int initialized = 0;
  108. static Tcl_HashTable masterTable;
  109. static Tcl_HashTable slaveTable;
  110. /*
  111.  * The following structure is the official type record for the
  112.  * placer:
  113.  */
  114.  
  115. static void        PlaceRequestProc _ANSI_ARGS_((ClientData clientData,
  116.                 Tk_Window tkwin));
  117. static void        PlaceLostSlaveProc _ANSI_ARGS_((ClientData clientData,
  118.                 Tk_Window tkwin));
  119.  
  120. static Tk_GeomMgr placerType = {
  121.     "place",                /* name */
  122.     PlaceRequestProc,            /* requestProc */
  123.     PlaceLostSlaveProc,            /* lostSlaveProc */
  124. };
  125.  
  126. /*
  127.  * Forward declarations for procedures defined later in this file:
  128.  */
  129.  
  130. static void        SlaveStructureProc _ANSI_ARGS_((ClientData clientData,
  131.                 XEvent *eventPtr));
  132. static int        ConfigureSlave _ANSI_ARGS_((Tcl_Interp *interp,
  133.                 Slave *slavePtr, int argc, char **argv));
  134. static Slave *        FindSlave _ANSI_ARGS_((Tk_Window tkwin));
  135. static Master *        FindMaster _ANSI_ARGS_((Tk_Window tkwin));
  136. static void        MasterStructureProc _ANSI_ARGS_((ClientData clientData,
  137.                 XEvent *eventPtr));
  138. static void        RecomputePlacement _ANSI_ARGS_((ClientData clientData));
  139. static void        UnlinkSlave _ANSI_ARGS_((Slave *slavePtr));
  140.  
  141. /*
  142.  *--------------------------------------------------------------
  143.  *
  144.  * Tk_PlaceCmd --
  145.  *
  146.  *    This procedure is invoked to process the "place" Tcl
  147.  *    commands.  See the user documentation for details on
  148.  *    what it does.
  149.  *
  150.  * Results:
  151.  *    A standard Tcl result.
  152.  *
  153.  * Side effects:
  154.  *    See the user documentation.
  155.  *
  156.  *--------------------------------------------------------------
  157.  */
  158.  
  159. int
  160. Tk_PlaceCmd(clientData, interp, argc, argv)
  161.     ClientData clientData;    /* Main window associated with interpreter. */
  162.     Tcl_Interp *interp;        /* Current interpreter. */
  163.     int argc;            /* Number of arguments. */
  164.     char **argv;        /* Argument strings. */
  165. {
  166.     Tk_Window tkwin;
  167.     Slave *slavePtr;
  168.     Tcl_HashEntry *hPtr;
  169.     size_t length;
  170.     int c;
  171.  
  172.     /*
  173.      * Initialize, if that hasn't been done yet.
  174.      */
  175.  
  176.     if (!initialized) {
  177.     Tcl_InitHashTable(&masterTable, TCL_ONE_WORD_KEYS);
  178.     Tcl_InitHashTable(&slaveTable, TCL_ONE_WORD_KEYS);
  179.     initialized = 1;
  180.     }
  181.  
  182.     if (argc < 3) {
  183.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  184.         argv[0], " option|pathName args", (char *) NULL);
  185.     return TCL_ERROR;
  186.     }
  187.     c = argv[1][0];
  188.     length = strlen(argv[1]);
  189.  
  190.     /*
  191.      * Handle special shortcut where window name is first argument.
  192.      */
  193.  
  194.     if (c == '.') {
  195.     tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
  196.     if (tkwin == NULL) {
  197.         return TCL_ERROR;
  198.     }
  199.     slavePtr = FindSlave(tkwin);
  200.     return ConfigureSlave(interp, slavePtr, argc-2, argv+2);
  201.     }
  202.  
  203.     /*
  204.      * Handle more general case of option followed by window name followed
  205.      * by possible additional arguments.
  206.      */
  207.  
  208.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  209.     if (tkwin == NULL) {
  210.     return TCL_ERROR;
  211.     }
  212.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  213.     if (argc < 5) {
  214.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  215.             argv[0],
  216.             " configure pathName option value ?option value ...?\"",
  217.             (char *) NULL);
  218.         return TCL_ERROR;
  219.     }
  220.     slavePtr = FindSlave(tkwin);
  221.     return ConfigureSlave(interp, slavePtr, argc-3, argv+3);
  222.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  223.     if (argc != 3) {
  224.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  225.             argv[0], " forget pathName\"", (char *) NULL);
  226.         return TCL_ERROR;
  227.     }
  228.     hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
  229.     if (hPtr == NULL) {
  230.         return TCL_OK;
  231.     }
  232.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  233.     if ((slavePtr->masterPtr != NULL) &&
  234.         (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) {
  235.         Tk_UnmaintainGeometry(slavePtr->tkwin,
  236.             slavePtr->masterPtr->tkwin);
  237.     }
  238.     UnlinkSlave(slavePtr);
  239.     Tcl_DeleteHashEntry(hPtr);
  240.     Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  241.         (ClientData) slavePtr);
  242.     Tk_ManageGeometry(tkwin, (Tk_GeomMgr *) NULL, (ClientData) NULL);
  243.     Tk_UnmapWindow(tkwin);
  244.     ckfree((char *) slavePtr);
  245.     } else if ((c == 'i') && (strncmp(argv[1], "info", length) == 0)) {
  246.     char buffer[50];
  247.  
  248.     if (argc != 3) {
  249.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  250.             argv[0], " info pathName\"", (char *) NULL);
  251.         return TCL_ERROR;
  252.     }
  253.     hPtr = Tcl_FindHashEntry(&slaveTable, (char *) tkwin);
  254.     if (hPtr == NULL) {
  255.         return TCL_OK;
  256.     }
  257.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  258.     sprintf(buffer, "-x %d", slavePtr->x);
  259.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  260.     sprintf(buffer, " -relx %.4g", slavePtr->relX);
  261.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  262.     sprintf(buffer, " -y %d", slavePtr->y);
  263.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  264.     sprintf(buffer, " -rely %.4g", slavePtr->relY);
  265.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  266.     if (slavePtr->flags & CHILD_WIDTH) {
  267.         sprintf(buffer, " -width %d", slavePtr->width);
  268.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  269.     } else {
  270.         Tcl_AppendResult(interp, " -width {}", (char *) NULL);
  271.     }
  272.     if (slavePtr->flags & CHILD_REL_WIDTH) {
  273.         sprintf(buffer, " -relwidth %.4g", slavePtr->relWidth);
  274.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  275.     } else {
  276.         Tcl_AppendResult(interp, " -relwidth {}", (char *) NULL);
  277.     }
  278.     if (slavePtr->flags & CHILD_HEIGHT) {
  279.         sprintf(buffer, " -height %d", slavePtr->height);
  280.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  281.     } else {
  282.         Tcl_AppendResult(interp, " -height {}", (char *) NULL);
  283.     }
  284.     if (slavePtr->flags & CHILD_REL_HEIGHT) {
  285.         sprintf(buffer, " -relheight %.4g", slavePtr->relHeight);
  286.         Tcl_AppendResult(interp, buffer, (char *) NULL);
  287.     } else {
  288.         Tcl_AppendResult(interp, " -relheight {}", (char *) NULL);
  289.     }
  290.  
  291.     Tcl_AppendResult(interp, " -anchor ", Tk_NameOfAnchor(slavePtr->anchor),
  292.         (char *) NULL);
  293.     if (slavePtr->borderMode == BM_OUTSIDE) {
  294.         Tcl_AppendResult(interp, " -bordermode outside", (char *) NULL);
  295.     } else if (slavePtr->borderMode == BM_IGNORE) {
  296.         Tcl_AppendResult(interp, " -bordermode ignore", (char *) NULL);
  297.     }
  298.     if ((slavePtr->masterPtr != NULL)
  299.         && (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin))) {
  300.         Tcl_AppendResult(interp, " -in ",
  301.             Tk_PathName(slavePtr->masterPtr->tkwin), (char *) NULL);
  302.     }
  303.     } else if ((c == 's') && (strncmp(argv[1], "slaves", length) == 0)) {
  304.     if (argc != 3) {
  305.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  306.             argv[0], " slaves pathName\"", (char *) NULL);
  307.         return TCL_ERROR;
  308.     }
  309.     hPtr = Tcl_FindHashEntry(&masterTable, (char *) tkwin);
  310.     if (hPtr != NULL) {
  311.         Master *masterPtr;
  312.         masterPtr = (Master *) Tcl_GetHashValue(hPtr);
  313.         for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  314.             slavePtr = slavePtr->nextPtr) {
  315.         Tcl_AppendElement(interp, Tk_PathName(slavePtr->tkwin));
  316.         }
  317.     }
  318.     } else {
  319.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  320.         "\": must be configure, forget, info, or slaves",
  321.         (char *) NULL);
  322.     return TCL_ERROR;
  323.     }
  324.     return TCL_OK;
  325. }
  326.  
  327. /*
  328.  *----------------------------------------------------------------------
  329.  *
  330.  * FindSlave --
  331.  *
  332.  *    Given a Tk_Window token, find the Slave structure corresponding
  333.  *    to that token (making a new one if necessary).
  334.  *
  335.  * Results:
  336.  *    None.
  337.  *
  338.  * Side effects:
  339.  *    A new Slave structure may be created.
  340.  *
  341.  *----------------------------------------------------------------------
  342.  */
  343.  
  344. static Slave *
  345. FindSlave(tkwin)
  346.     Tk_Window tkwin;        /* Token for desired slave. */
  347. {
  348.     Tcl_HashEntry *hPtr;
  349.     register Slave *slavePtr;
  350.     int new;
  351.  
  352.     hPtr = Tcl_CreateHashEntry(&slaveTable, (char *) tkwin, &new);
  353.     if (new) {
  354.     slavePtr = (Slave *) ckalloc(sizeof(Slave));
  355.     slavePtr->tkwin = tkwin;
  356.     slavePtr->masterPtr = NULL;
  357.     slavePtr->nextPtr = NULL;
  358.     slavePtr->x = slavePtr->y = 0;
  359.     slavePtr->relX = slavePtr->relY = 0.0;
  360.     slavePtr->width = slavePtr->height = 0;
  361.     slavePtr->relWidth = slavePtr->relHeight = 0.0;
  362.     slavePtr->anchor = TK_ANCHOR_NW;
  363.     slavePtr->borderMode = BM_INSIDE;
  364.     slavePtr->flags = 0;
  365.     Tcl_SetHashValue(hPtr, slavePtr);
  366.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  367.         (ClientData) slavePtr);
  368.     Tk_ManageGeometry(tkwin, &placerType, (ClientData) slavePtr);
  369.     } else {
  370.     slavePtr = (Slave *) Tcl_GetHashValue(hPtr);
  371.     }
  372.     return slavePtr;
  373. }
  374.  
  375. /*
  376.  *----------------------------------------------------------------------
  377.  *
  378.  * UnlinkSlave --
  379.  *
  380.  *    This procedure removes a slave window from the chain of slaves
  381.  *    in its master.
  382.  *
  383.  * Results:
  384.  *    None.
  385.  *
  386.  * Side effects:
  387.  *    The slave list of slavePtr's master changes.
  388.  *
  389.  *----------------------------------------------------------------------
  390.  */
  391.  
  392. static void
  393. UnlinkSlave(slavePtr)
  394.     Slave *slavePtr;        /* Slave structure to be unlinked. */
  395. {
  396.     register Master *masterPtr;
  397.     register Slave *prevPtr;
  398.  
  399.     masterPtr = slavePtr->masterPtr;
  400.     if (masterPtr == NULL) {
  401.     return;
  402.     }
  403.     if (masterPtr->slavePtr == slavePtr) {
  404.     masterPtr->slavePtr = slavePtr->nextPtr;
  405.     } else {
  406.     for (prevPtr = masterPtr->slavePtr; ;
  407.         prevPtr = prevPtr->nextPtr) {
  408.         if (prevPtr == NULL) {
  409.         panic("UnlinkSlave couldn't find slave to unlink");
  410.         }
  411.         if (prevPtr->nextPtr == slavePtr) {
  412.         prevPtr->nextPtr = slavePtr->nextPtr;
  413.         break;
  414.         }
  415.     }
  416.     }
  417.     slavePtr->masterPtr = NULL;
  418. }
  419.  
  420. /*
  421.  *----------------------------------------------------------------------
  422.  *
  423.  * FindMaster --
  424.  *
  425.  *    Given a Tk_Window token, find the Master structure corresponding
  426.  *    to that token (making a new one if necessary).
  427.  *
  428.  * Results:
  429.  *    None.
  430.  *
  431.  * Side effects:
  432.  *    A new Master structure may be created.
  433.  *
  434.  *----------------------------------------------------------------------
  435.  */
  436.  
  437. static Master *
  438. FindMaster(tkwin)
  439.     Tk_Window tkwin;        /* Token for desired master. */
  440. {
  441.     Tcl_HashEntry *hPtr;
  442.     register Master *masterPtr;
  443.     int new;
  444.  
  445.     hPtr = Tcl_CreateHashEntry(&masterTable, (char *) tkwin, &new);
  446.     if (new) {
  447.     masterPtr = (Master *) ckalloc(sizeof(Master));
  448.     masterPtr->tkwin = tkwin;
  449.     masterPtr->slavePtr = NULL;
  450.     masterPtr->flags = 0;
  451.     Tcl_SetHashValue(hPtr, masterPtr);
  452.     Tk_CreateEventHandler(masterPtr->tkwin, StructureNotifyMask,
  453.         MasterStructureProc, (ClientData) masterPtr);
  454.     } else {
  455.     masterPtr = (Master *) Tcl_GetHashValue(hPtr);
  456.     }
  457.     return masterPtr;
  458. }
  459.  
  460. /*
  461.  *----------------------------------------------------------------------
  462.  *
  463.  * ConfigureSlave --
  464.  *
  465.  *    This procedure is called to process an argv/argc list to
  466.  *    reconfigure the placement of a window.
  467.  *
  468.  * Results:
  469.  *    A standard Tcl result.  If an error occurs then a message is
  470.  *    left in interp->result.
  471.  *
  472.  * Side effects:
  473.  *    Information in slavePtr may change, and slavePtr's master is
  474.  *    scheduled for reconfiguration.
  475.  *
  476.  *----------------------------------------------------------------------
  477.  */
  478.  
  479. static int
  480. ConfigureSlave(interp, slavePtr, argc, argv)
  481.     Tcl_Interp *interp;        /* Used for error reporting. */
  482.     Slave *slavePtr;        /* Pointer to current information
  483.                  * about slave. */
  484.     int argc;            /* Number of config arguments. */
  485.     char **argv;        /* String values for arguments. */
  486. {
  487.     register Master *masterPtr;
  488.     int c, result;
  489.     size_t length;
  490.     double d;
  491.  
  492.     result = TCL_OK;
  493.     if (Tk_IsTopLevel(slavePtr->tkwin)) {
  494.     Tcl_AppendResult(interp, "can't use placer on top-level window \"",
  495.         Tk_PathName(slavePtr->tkwin), "\"; use wm command instead",
  496.         (char *) NULL);
  497.     return TCL_ERROR;
  498.     }
  499.     for ( ; argc > 0; argc -= 2, argv += 2) {
  500.     if (argc < 2) {
  501.         Tcl_AppendResult(interp, "extra option \"", argv[0],
  502.             "\" (option with no value?)", (char *) NULL);
  503.         result = TCL_ERROR;
  504.         goto done;
  505.     }
  506.     length = strlen(argv[0]);
  507.     c = argv[0][1];
  508.     if ((c == 'a') && (strncmp(argv[0], "-anchor", length) == 0)) {
  509.         if (Tk_GetAnchor(interp, argv[1], &slavePtr->anchor) != TCL_OK) {
  510.         result = TCL_ERROR;
  511.         goto done;
  512.         }
  513.     } else if ((c == 'b')
  514.         && (strncmp(argv[0], "-bordermode", length) == 0)) {
  515.         c = argv[1][0];
  516.         length = strlen(argv[1]);
  517.         if ((c == 'i') && (strncmp(argv[1], "ignore", length) == 0)
  518.             && (length >= 2)) {
  519.         slavePtr->borderMode = BM_IGNORE;
  520.         } else if ((c == 'i') && (strncmp(argv[1], "inside", length) == 0)
  521.             && (length >= 2)) {
  522.         slavePtr->borderMode = BM_INSIDE;
  523.         } else if ((c == 'o')
  524.             && (strncmp(argv[1], "outside", length) == 0)) {
  525.         slavePtr->borderMode = BM_OUTSIDE;
  526.         } else {
  527.         Tcl_AppendResult(interp, "bad border mode \"", argv[1],
  528.             "\": must be ignore, inside, or outside",
  529.             (char *) NULL);
  530.         result = TCL_ERROR;
  531.         goto done;
  532.         }
  533.     } else if ((c == 'h') && (strncmp(argv[0], "-height", length) == 0)) {
  534.         if (argv[1][0] == 0) {
  535.         slavePtr->flags &= ~CHILD_HEIGHT;
  536.         } else {
  537.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  538.             &slavePtr->height) != TCL_OK) {
  539.             result = TCL_ERROR;
  540.             goto done;
  541.         }
  542.         slavePtr->flags |= CHILD_HEIGHT;
  543.         }
  544.     } else if ((c == 'i') && (strncmp(argv[0], "-in", length) == 0)) {
  545.         Tk_Window tkwin;
  546.         Tk_Window ancestor;
  547.  
  548.         tkwin = Tk_NameToWindow(interp, argv[1], slavePtr->tkwin);
  549.         if (tkwin == NULL) {
  550.         result = TCL_ERROR;
  551.         goto done;
  552.         }
  553.  
  554.         /*
  555.          * Make sure that the new master is either the logical parent
  556.          * of the slave or a descendant of that window.
  557.          */
  558.  
  559.         for (ancestor = tkwin; ; ancestor = Tk_Parent(ancestor)) {
  560.         if (ancestor == Tk_Parent(slavePtr->tkwin)) {
  561.             break;
  562.         }
  563.         if (Tk_IsTopLevel(ancestor)) {
  564.             Tcl_AppendResult(interp, "can't place ",
  565.                 Tk_PathName(slavePtr->tkwin), " relative to ",
  566.                 Tk_PathName(tkwin), (char *) NULL);
  567.             result = TCL_ERROR;
  568.             goto done;
  569.         }
  570.         }
  571.         if ((slavePtr->masterPtr != NULL)
  572.             && (slavePtr->masterPtr->tkwin == tkwin)) {
  573.         /*
  574.          * Re-using same old master.  Nothing to do.
  575.          */
  576.         } else {
  577.         if ((slavePtr->masterPtr != NULL)
  578.             && (slavePtr->masterPtr->tkwin
  579.             != Tk_Parent(slavePtr->tkwin))) {
  580.             Tk_UnmaintainGeometry(slavePtr->tkwin,
  581.                 slavePtr->masterPtr->tkwin);
  582.         }
  583.         UnlinkSlave(slavePtr);
  584.         slavePtr->masterPtr = FindMaster(tkwin);
  585.         slavePtr->nextPtr = slavePtr->masterPtr->slavePtr;
  586.         slavePtr->masterPtr->slavePtr = slavePtr;
  587.         }
  588.     } else if ((c == 'r') && (strncmp(argv[0], "-relheight", length) == 0)
  589.         && (length >= 5)) {
  590.         if (argv[1][0] == 0) {
  591.         slavePtr->flags &= ~CHILD_REL_HEIGHT;
  592.         } else {
  593.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  594.             result = TCL_ERROR;
  595.             goto done;
  596.         }
  597.         slavePtr->relHeight = d;
  598.         slavePtr->flags |= CHILD_REL_HEIGHT;
  599.         }
  600.     } else if ((c == 'r') && (strncmp(argv[0], "-relwidth", length) == 0)
  601.         && (length >= 5)) {
  602.         if (argv[1][0] == 0) {
  603.         slavePtr->flags &= ~CHILD_REL_WIDTH;
  604.         } else {
  605.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  606.             result = TCL_ERROR;
  607.             goto done;
  608.         }
  609.         slavePtr->relWidth = d;
  610.         slavePtr->flags |= CHILD_REL_WIDTH;
  611.         }
  612.     } else if ((c == 'r') && (strncmp(argv[0], "-relx", length) == 0)
  613.         && (length >= 5)) {
  614.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  615.         result = TCL_ERROR;
  616.         goto done;
  617.         }
  618.         slavePtr->relX = d;
  619.     } else if ((c == 'r') && (strncmp(argv[0], "-rely", length) == 0)
  620.         && (length >= 5)) {
  621.         if (Tcl_GetDouble(interp, argv[1], &d) != TCL_OK) {
  622.         result = TCL_ERROR;
  623.         goto done;
  624.         }
  625.         slavePtr->relY = d;
  626.     } else if ((c == 'w') && (strncmp(argv[0], "-width", length) == 0)) {
  627.         if (argv[1][0] == 0) {
  628.         slavePtr->flags &= ~CHILD_WIDTH;
  629.         } else {
  630.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  631.             &slavePtr->width) != TCL_OK) {
  632.             result = TCL_ERROR;
  633.             goto done;
  634.         }
  635.         slavePtr->flags |= CHILD_WIDTH;
  636.         }
  637.     } else if ((c == 'x') && (strncmp(argv[0], "-x", length) == 0)) {
  638.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  639.             &slavePtr->x) != TCL_OK) {
  640.         result = TCL_ERROR;
  641.         goto done;
  642.         }
  643.     } else if ((c == 'y') && (strncmp(argv[0], "-y", length) == 0)) {
  644.         if (Tk_GetPixels(interp, slavePtr->tkwin, argv[1],
  645.             &slavePtr->y) != TCL_OK) {
  646.         result = TCL_ERROR;
  647.         goto done;
  648.         }
  649.     } else {
  650.         Tcl_AppendResult(interp, "unknown or ambiguous option \"",
  651.             argv[0], "\": must be -anchor, -bordermode, -height, ",
  652.             "-in, -relheight, -relwidth, -relx, -rely, -width, ",
  653.             "-x, or -y", (char *) NULL);
  654.         result = TCL_ERROR;
  655.         goto done;
  656.     }
  657.     }
  658.  
  659.     /*
  660.      * If there's no master specified for this slave, use its Tk_Parent.
  661.      * Then arrange for a placement recalculation in the master.
  662.      */
  663.  
  664.     done:
  665.     masterPtr = slavePtr->masterPtr;
  666.     if (masterPtr == NULL) {
  667.     masterPtr = FindMaster(Tk_Parent(slavePtr->tkwin));
  668.     slavePtr->masterPtr = masterPtr;
  669.     slavePtr->nextPtr = masterPtr->slavePtr;
  670.     masterPtr->slavePtr = slavePtr;
  671.     }
  672.     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  673.     masterPtr->flags |= PARENT_RECONFIG_PENDING;
  674.     Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  675.     }
  676.     return result;
  677. }
  678.  
  679. /*
  680.  *----------------------------------------------------------------------
  681.  *
  682.  * RecomputePlacement --
  683.  *
  684.  *    This procedure is called as a when-idle handler.  It recomputes
  685.  *    the geometries of all the slaves of a given master.
  686.  *
  687.  * Results:
  688.  *    None.
  689.  *
  690.  * Side effects:
  691.  *    Windows may change size or shape.
  692.  *
  693.  *----------------------------------------------------------------------
  694.  */
  695.  
  696. static void
  697. RecomputePlacement(clientData)
  698.     ClientData clientData;    /* Pointer to Master record. */
  699. {
  700.     register Master *masterPtr = (Master *) clientData;
  701.     register Slave *slavePtr;
  702.     int x, y, width, height, tmp;
  703.     int masterWidth, masterHeight, masterBW;
  704.     double x1, y1, x2, y2;
  705.  
  706.     masterPtr->flags &= ~PARENT_RECONFIG_PENDING;
  707.  
  708.     /*
  709.      * Iterate over all the slaves for the master.  Each slave's
  710.      * geometry can be computed independently of the other slaves.
  711.      */
  712.  
  713.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  714.         slavePtr = slavePtr->nextPtr) {
  715.     /*
  716.      * Step 1: compute size and borderwidth of master, taking into
  717.      * account desired border mode.
  718.      */
  719.  
  720.     masterBW = 0;
  721.     masterWidth = Tk_Width(masterPtr->tkwin);
  722.     masterHeight = Tk_Height(masterPtr->tkwin);
  723.     if (slavePtr->borderMode == BM_INSIDE) {
  724.         masterBW = Tk_InternalBorderWidth(masterPtr->tkwin);
  725.     } else if (slavePtr->borderMode == BM_OUTSIDE) {
  726.         masterBW = -Tk_Changes(masterPtr->tkwin)->border_width;
  727.     }
  728.     masterWidth -= 2*masterBW;
  729.     masterHeight -= 2*masterBW;
  730.  
  731.     /*
  732.      * Step 2:  compute size of slave (outside dimensions including
  733.      * border) and location of anchor point within master.
  734.      */
  735.  
  736.     x1 = slavePtr->x + masterBW + (slavePtr->relX*masterWidth);
  737.     x = x1 + ((x1 > 0) ? 0.5 : -0.5);
  738.     y1 = slavePtr->y + masterBW + (slavePtr->relY*masterHeight);
  739.     y = y1 + ((y1 > 0) ? 0.5 : -0.5);
  740.     if (slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) {
  741.         width = 0;
  742.         if (slavePtr->flags & CHILD_WIDTH) {
  743.         width += slavePtr->width;
  744.         }
  745.         if (slavePtr->flags & CHILD_REL_WIDTH) {
  746.         /*
  747.          * The code below is a bit tricky.  In order to round
  748.          * correctly when both relX and relWidth are specified,
  749.          * compute the location of the right edge and round that,
  750.          * then compute width.  If we compute the width and round
  751.          * it, rounding errors in relX and relWidth accumulate.
  752.          */
  753.  
  754.         x2 = x1 + (slavePtr->relWidth*masterWidth);
  755.         tmp = x2 + ((x2 > 0) ? 0.5 : -0.5);
  756.         width += tmp - x;
  757.         }
  758.     } else {
  759.         width = Tk_ReqWidth(slavePtr->tkwin)
  760.             + 2*Tk_Changes(slavePtr->tkwin)->border_width;
  761.     }
  762.     if (slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) {
  763.         height = 0;
  764.         if (slavePtr->flags & CHILD_HEIGHT) {
  765.         height += slavePtr->height;
  766.         }
  767.         if (slavePtr->flags & CHILD_REL_HEIGHT) {
  768.         /* 
  769.          * See note above for rounding errors in width computation.
  770.          */
  771.  
  772.         y2 = y1 + (slavePtr->relHeight*masterHeight);
  773.         tmp = y2 + ((y2 > 0) ? 0.5 : -0.5);
  774.         height += tmp - y;
  775.         }
  776.     } else {
  777.         height = Tk_ReqHeight(slavePtr->tkwin)
  778.             + 2*Tk_Changes(slavePtr->tkwin)->border_width;
  779.     }
  780.  
  781.     /*
  782.      * Step 3: adjust the x and y positions so that the desired
  783.      * anchor point on the slave appears at that position.  Also
  784.      * adjust for the border mode and master's border.
  785.      */
  786.  
  787.     switch (slavePtr->anchor) {
  788.         case TK_ANCHOR_N:
  789.         x -= width/2;
  790.         break;
  791.         case TK_ANCHOR_NE:
  792.         x -= width;
  793.         break;
  794.         case TK_ANCHOR_E:
  795.         x -= width;
  796.         y -= height/2;
  797.         break;
  798.         case TK_ANCHOR_SE:
  799.         x -= width;
  800.         y -= height;
  801.         break;
  802.         case TK_ANCHOR_S:
  803.         x -= width/2;
  804.         y -= height;
  805.         break;
  806.         case TK_ANCHOR_SW:
  807.         y -= height;
  808.         break;
  809.         case TK_ANCHOR_W:
  810.         y -= height/2;
  811.         break;
  812.         case TK_ANCHOR_NW:
  813.         break;
  814.         case TK_ANCHOR_CENTER:
  815.         x -= width/2;
  816.         y -= height/2;
  817.         break;
  818.     }
  819.  
  820.     /*
  821.      * Step 4: adjust width and height again to reflect inside dimensions
  822.      * of window rather than outside.  Also make sure that the width and
  823.      * height aren't zero.
  824.      */
  825.  
  826.     width -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
  827.     height -= 2*Tk_Changes(slavePtr->tkwin)->border_width;
  828.     if (width <= 0) {
  829.         width = 1;
  830.     }
  831.     if (height <= 0) {
  832.         height = 1;
  833.     }
  834.  
  835.     /*
  836.      * Step 5: reconfigure the window and map it if needed.  If the
  837.      * slave is a child of the master, we do this ourselves.  If the
  838.      * slave isn't a child of the master, let Tk_MaintainWindow do
  839.      * the work (it will re-adjust things as relevant windows map,
  840.      * unmap, and move).
  841.      */
  842.  
  843.     if (masterPtr->tkwin == Tk_Parent(slavePtr->tkwin)) {
  844.         if ((x != Tk_X(slavePtr->tkwin))
  845.             || (y != Tk_Y(slavePtr->tkwin))
  846.             || (width != Tk_Width(slavePtr->tkwin))
  847.             || (height != Tk_Height(slavePtr->tkwin))) {
  848.         Tk_MoveResizeWindow(slavePtr->tkwin, x, y, width, height);
  849.         }
  850.         Tk_MapWindow(slavePtr->tkwin);
  851.     } else {
  852.         if ((width <= 0) || (height <= 0)) {
  853.         Tk_UnmaintainGeometry(slavePtr->tkwin, masterPtr->tkwin);
  854.         Tk_UnmapWindow(slavePtr->tkwin);
  855.         } else {
  856.         Tk_MaintainGeometry(slavePtr->tkwin, masterPtr->tkwin,
  857.             x, y, width, height);
  858.         }
  859.     }
  860.     }
  861. }
  862.  
  863. /*
  864.  *----------------------------------------------------------------------
  865.  *
  866.  * MasterStructureProc --
  867.  *
  868.  *    This procedure is invoked by the Tk event handler when
  869.  *    StructureNotify events occur for a master window.
  870.  *
  871.  * Results:
  872.  *    None.
  873.  *
  874.  * Side effects:
  875.  *    Structures get cleaned up if the window was deleted.  If the
  876.  *    window was resized then slave geometries get recomputed.
  877.  *
  878.  *----------------------------------------------------------------------
  879.  */
  880.  
  881. static void
  882. MasterStructureProc(clientData, eventPtr)
  883.     ClientData clientData;    /* Pointer to Master structure for window
  884.                  * referred to by eventPtr. */
  885.     XEvent *eventPtr;        /* Describes what just happened. */
  886. {
  887.     register Master *masterPtr = (Master *) clientData;
  888.     register Slave *slavePtr, *nextPtr;
  889.  
  890.     if (eventPtr->type == ConfigureNotify) {
  891.     if ((masterPtr->slavePtr != NULL)
  892.         && !(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  893.         masterPtr->flags |= PARENT_RECONFIG_PENDING;
  894.         Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  895.     }
  896.     } else if (eventPtr->type == DestroyNotify) {
  897.     for (slavePtr = masterPtr->slavePtr; slavePtr != NULL;
  898.         slavePtr = nextPtr) {
  899.         slavePtr->masterPtr = NULL;
  900.         nextPtr = slavePtr->nextPtr;
  901.         slavePtr->nextPtr = NULL;
  902.     }
  903.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&masterTable,
  904.         (char *) masterPtr->tkwin));
  905.     if (masterPtr->flags & PARENT_RECONFIG_PENDING) {
  906.         Tk_CancelIdleCall(RecomputePlacement, (ClientData) masterPtr);
  907.     }
  908.     masterPtr->tkwin = NULL;
  909.     ckfree((char *) masterPtr);
  910.     }
  911. }
  912.  
  913. /*
  914.  *----------------------------------------------------------------------
  915.  *
  916.  * SlaveStructureProc --
  917.  *
  918.  *    This procedure is invoked by the Tk event handler when
  919.  *    StructureNotify events occur for a slave window.
  920.  *
  921.  * Results:
  922.  *    None.
  923.  *
  924.  * Side effects:
  925.  *    Structures get cleaned up if the window was deleted.
  926.  *
  927.  *----------------------------------------------------------------------
  928.  */
  929.  
  930. static void
  931. SlaveStructureProc(clientData, eventPtr)
  932.     ClientData clientData;    /* Pointer to Slave structure for window
  933.                  * referred to by eventPtr. */
  934.     XEvent *eventPtr;        /* Describes what just happened. */
  935. {
  936.     register Slave *slavePtr = (Slave *) clientData;
  937.  
  938.     if (eventPtr->type == DestroyNotify) {
  939.     UnlinkSlave(slavePtr);
  940.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable,
  941.         (char *) slavePtr->tkwin));
  942.     ckfree((char *) slavePtr);
  943.     }
  944. }
  945.  
  946. /*
  947.  *----------------------------------------------------------------------
  948.  *
  949.  * PlaceRequestProc --
  950.  *
  951.  *    This procedure is invoked by Tk whenever a slave managed by us
  952.  *    changes its requested geometry.
  953.  *
  954.  * Results:
  955.  *    None.
  956.  *
  957.  * Side effects:
  958.  *    The window will get relayed out, if its requested size has
  959.  *    anything to do with its actual size.
  960.  *
  961.  *----------------------------------------------------------------------
  962.  */
  963.  
  964.     /* ARGSUSED */
  965. static void
  966. PlaceRequestProc(clientData, tkwin)
  967.     ClientData clientData;        /* Pointer to our record for slave. */
  968.     Tk_Window tkwin;            /* Window that changed its desired
  969.                      * size. */
  970. {
  971.     Slave *slavePtr = (Slave *) clientData;
  972.     Master *masterPtr;
  973.  
  974.     if (((slavePtr->flags & (CHILD_WIDTH|CHILD_REL_WIDTH)) != 0)
  975.         && ((slavePtr->flags & (CHILD_HEIGHT|CHILD_REL_HEIGHT)) != 0)) {
  976.     return;
  977.     }
  978.     masterPtr = slavePtr->masterPtr;
  979.     if (masterPtr == NULL) {
  980.     return;
  981.     }
  982.     if (!(masterPtr->flags & PARENT_RECONFIG_PENDING)) {
  983.     masterPtr->flags |= PARENT_RECONFIG_PENDING;
  984.     Tk_DoWhenIdle(RecomputePlacement, (ClientData) masterPtr);
  985.     }
  986. }
  987.  
  988. /*
  989.  *--------------------------------------------------------------
  990.  *
  991.  * PlaceLostSlaveProc --
  992.  *
  993.  *    This procedure is invoked by Tk whenever some other geometry
  994.  *    claims control over a slave that used to be managed by us.
  995.  *
  996.  * Results:
  997.  *    None.
  998.  *
  999.  * Side effects:
  1000.  *    Forgets all placer-related information about the slave.
  1001.  *
  1002.  *--------------------------------------------------------------
  1003.  */
  1004.  
  1005.     /* ARGSUSED */
  1006. static void
  1007. PlaceLostSlaveProc(clientData, tkwin)
  1008.     ClientData clientData;    /* Slave structure for slave window that
  1009.                  * was stolen away. */
  1010.     Tk_Window tkwin;        /* Tk's handle for the slave window. */
  1011. {
  1012.     register Slave *slavePtr = (Slave *) clientData;
  1013.  
  1014.     if (slavePtr->masterPtr->tkwin != Tk_Parent(slavePtr->tkwin)) {
  1015.     Tk_UnmaintainGeometry(slavePtr->tkwin, slavePtr->masterPtr->tkwin);
  1016.     }
  1017.     Tk_UnmapWindow(tkwin);
  1018.     UnlinkSlave(slavePtr);
  1019.     Tcl_DeleteHashEntry(Tcl_FindHashEntry(&slaveTable, (char *) tkwin));
  1020.     Tk_DeleteEventHandler(tkwin, StructureNotifyMask, SlaveStructureProc,
  1021.         (ClientData) slavePtr);
  1022.     ckfree((char *) slavePtr);
  1023. }
  1024.